<?php

namespace app\admin\server;

//需要使用到的类


use Exception;
use Psr\SimpleCache\InvalidArgumentException;
use think\facade\Config;
use think\facade\Log;

trait RedisServerTrait
{
    /**删除缓存
     * @param $redis_table
     * @return void
     * @throws InvalidArgumentException
     */
    public static function deleteCache($redis_table)
    {
        try {
            $redis = RedisServer::getInstance();
            if($redis_table) {
                if(is_string($redis_table)) {
                    $redis->delete($redis_table);
                } elseif(is_array($redis_table)) {
                    foreach($redis_table as $item) {
                        if(isset($item['redis_name']) && $item['redis_name']) {
                            $redis->delete($item['redis_name']);
                        }
                    }
                }
            }
        } catch(Exception $e) {
            Log::write('deleteCache:::' . $redis_table . ':::' . $e->getMessage(), 'error');
        }
    }


    /**删除 该表 相关的所有缓存
     * @return void
     * @throws InvalidArgumentException
     */
    public static function deleteTableCache()
    {
        $class_name = get_class();
        $obj = new  $class_name();
        $redis_table = $obj->redis_table;
        if($obj->redis_name && $redis_table) {
            self::deleteCache($redis_table);
        }
    }


    /**新增后 将  该表 数据添加到redis
     * @param $info
     * @return void
     * @throws InvalidArgumentException
     */
    public static function onAfterInsert($info)
    {
        self::deleteTableCache();
    }


    /**更新后 将  该表 数据更新到redis
     * @param $info
     * @return void
     * @throws InvalidArgumentException
     */
    public static function onAfterUpdate($info)
    {
        self::deleteTableCache();
    }


    /**删除后 将 该表 数据更新到redis
     * @param $info
     * @return void
     * @throws InvalidArgumentException
     */
    public static function onAfterDelete($info)
    {
        self::deleteTableCache();
    }

    /**恢复后 将 该表 数据更新到redis
     * @param $info
     * @return void
     * @throws InvalidArgumentException
     */
    public static function onAfterRestore($info)
    {
        self::deleteTableCache();
    }

    /**
     * @return void
     * @throws InvalidArgumentException
     */
    public static function setRedisAll()
    {
        $class_name = get_class();
        $obj = new $class_name();
        $redis_name = $obj->redis_table;
        if($obj->redis_name && $redis_name) {
            if(is_string($redis_name)) {
                self::setRedisData($redis_name);
            } elseif(is_array($redis_name)) {
                foreach($redis_name as $item) {
                    $item_redis_name = $item['redis_name'] ?? '';
                    $where = $item['where'] ?? [];

                    if(!isset($item['redis_field']) || !$item['redis_field']) {
                        $redis_field = $obj->redis_field;
                    } else {
                        $redis_field = $item['redis_field'];
                    }
                    $order = $item['order'] ?? [];
                    $group = $item['group'] ?? '';
                    $limit = $item['limit'] ?? 1000;
                    $item_redis_name && self::setRedisData($item_redis_name, $where, $redis_field, $order, $group, $limit);
                }
            }
        }
    }

    /**
     * @return false|string
     */
    public static function getTableUpdateCacheName(string $redis_name = '')
    {
        $class_name = get_class();
        $obj = new $class_name();
        if(!$obj->redis_name) {
            return false;
        }
        return ($redis_name ?: $obj->redis_name) . '_cache_time';//数据表数据最后一次更新时间
    }

    public static function setRedisLatestTime($update_time = '')
    {
        $class_name = get_class();
        $obj = new $class_name();
        $table_update_cache_name = self::getTableUpdateCacheName();//数据表数据最后一次更新时间

        !$update_time && $update_time = $obj->order($obj->redis_latest_time_field, 'desc')->value($obj->redis_latest_time_field);

        $redis = RedisServer::getInstance();
        $redis->set($table_update_cache_name, $update_time);
    }

    /**
     * @param        $redis_name
     * @param array  $where
     * @param array  $redis_field
     * @param array  $orders
     * @param string $group
     * @param int    $limit
     * @return false|string
     * @throws InvalidArgumentException
     */
    public static function setRedisData($redis_name, array $where = [], array $redis_field = [], array $orders = [], string $group = '', int $limit = 1000, $update_time = '')
    {
        if(!$redis_name) {
            return false;
        }
        $class_name = get_class();
        $obj = new $class_name();
        if(!$obj->redis_name) {
            return false;
        }
        $redis = RedisServer::getInstance();
        try {

            self::setRedisLatestTime($update_time);
            $obj = new $class_name();

            if(!$redis_field) {
                $redis_field = $obj->redis_field;
                !$redis_field && $redis_field = '*';
            }

            $obj = $obj->where($where)->field($redis_field);

            if($orders) {
                foreach($orders as $item) {
                    if(count($item) == 1) {
                        $obj = $obj->order($item[0], 'asc');
                    } else if(count($item) == 2) {
                        $obj = $obj->order($item[0], $item[1]);
                    }
                }
            }
            if($group) {
                $obj = $obj->whereNotNull($group);
            }
            if($limit) {
                $obj = $obj->limit($limit);
            }
            $data = $obj->select();

            if(!$data || $data->isEmpty()) {
                $pipe = $redis->pipeline(); //开启管道
                $redis->set($redis_name, 'null');
                $pipe->exec(); //提交管道里操作命令
                return '';
            }

            $data = $data->toArray();
            $group_ids = null;
            if($group) {
                $obj = new  $class_name();
                $group_ids = $obj->distinct(true)->whereNotNull($group)->column($group, $obj->getPk());
                if(!$group_ids) {
                    $redis->set($redis_name, 'null');
                    return '';
                }
            }
            $_item = array_flip($redis_field);

            $data_new = [];
            foreach($data as $item) {
                if(!isset($item[$obj->getPk()])) {
                    break;
                }
                foreach($_item as $key => $value) {
                    $_item[$key] = $item[$key] ?? '';
                }
                if($group) {
                    if(!isset($item[$group])) {
                        continue;
                    }
                    $data_new[$item[$group]][$item[$obj->getPk()]] = $_item;
                } else {
                    $data_new[$item[$obj->getPk()]] = $_item;
                }
            }
            if($group && !$data_new) {
                $pipe = $redis->pipeline(); //开启管道
                $redis->set($redis_name, 'null');
                $pipe->exec(); //提交管道里操作命令
            }

            foreach($data_new as &$item) {
                $item = json_encode($item, JSON_UNESCAPED_UNICODE);
            }
            if($group && $group_ids) {
                foreach($group_ids as $group_id) {
                    if(!isset($data_new[$group_id])) {
                        $data_new[$group_id] = 'null';
                    }
                }
                unset($item);
            }
            self::setRedis($redis_name, $data_new);
        } catch(Exception $e) {
            Log::write('setRedisData::' . $redis_name . '::' . $e->getMessage(), 'error');
        }
        return '';
    }

    /**
     * @param $redis_name
     * @param $data_new
     * @param $other
     * @return void
     */
    public static function setRedis($redis_name, $data_new, $other = [])
    {
        $redis = RedisServer::getInstance();
        $pipe = $redis->pipeline(); //开启管道
        $redis->hMSet($redis_name, $data_new);
        $pipe->exec(); //提交管道里操作命令
    }

    /**
     * @param     $group
     * @param int $group_id
     * @return array
     * @throws InvalidArgumentException
     */
    public static function getCacheListByGroup($group, int $group_id): array
    {
        $class_name = get_class();
        $obj = new $class_name();
        $redis_name = $obj->redis_table;
        if(!$obj->redis_name) {
            return [];
        }
        if($redis_name && is_array($redis_name)) {
            foreach($redis_name as $item) {
                $redis_name = $item['redis_name'] ?? '';
//                $where = isset($item['where']) ? $item['where'] : [];
//                $redis_field = isset($item['redis_field']) ? $item['redis_field'] : [];
//                $order = isset($item['order']) ? $item['order'] : [];
                $group_cache = $item['group'] ?? '';

                if($group == $group_cache) {
                    return self::getRedisListByCacheName($redis_name, $group_id);
                }
            }
        }
        return [];
    }

    /***
     * @param $cache_name
     * @return array
     * @throws InvalidArgumentException
     */
    public static function getCacheAll($cache_name): array
    {
        $class_name = get_class();
        $obj = new $class_name();
        if(!$obj->redis_name) {
            return [];
        }
        if(!self::checkRedisLatestTableUpdateTime()) {
            self::deleteCache($cache_name);
            self::setRedisAll();
            return self::getCacheAll($cache_name);
        }
        $redis = RedisServer::getInstance();
        if(!$redis->exists($cache_name)) {
            $redis_prefix = Config::get('cache.stores.redis.prefix');
            $cache_name_new = $redis_prefix . $cache_name;
            if(!$redis->exists($cache_name_new)) {
                self::deleteCache($cache_name);
                self::setRedisAll();
                return self::getCacheAll($cache_name);
            }
        }
        $data = $redis->hGetAll($cache_name);
        if(!$data) {
            return [];
        }
        $r_data = [];
        foreach($data as $item) {
            $r_data[] = json_decode($item, true);
        }
        return $r_data;
    }

    /**
     * @param     $redis_name
     * @param int $id
     * @return array
     * @throws InvalidArgumentException
     */
    public static function getRedisListByCacheName($redis_name, int $id): array
    {
        $class_name = get_class();
        $obj = new $class_name();
        $redis_table = $obj->redis_table;
        if(!$redis_table) {
            return [];
        }
        if(!$obj->redis_name) {
            return [];
        }
        if(!self::checkRedisLatestTableUpdateTime()) {
            self::deleteCache($redis_table);
            self::setRedisAll();
            return self::getRedisListByCacheName($redis_name, $id);
        }
        $redis = RedisServer::getInstance();
        if(!$redis->hExists($redis_name, $id)) {
            if(!$redis->exists($redis_name)) {
                self::deleteCache($redis_table);
                self::setRedisAll();
                return self::getRedisListByCacheName($redis_name, $id);
            } else {
                $data = $redis->get($redis_name);
                if(empty($data)) {
                    return [];
                }
            }
        }
        $data = $redis->hGet($redis_name, $id);
        return $data ? json_decode($data, true) : [];
    }

    /**检查 缓存的数据是否是最新的
     * @return bool
     * @throws InvalidArgumentException
     */
    public static function checkRedisLatestTableUpdateTime(string $redis_name = '')
    {
        $class_name = get_class();
        $obj = new $class_name();
        $redis = RedisServer::getInstance();

        if(!$obj->redis_name) {
            return false;
        }
        $table_update_cache_name = self::getTableUpdateCacheName($redis_name);//数据表数据最后一次更新时间
        $update_time_cache = $redis->get($table_update_cache_name, '');
        if(!$update_time_cache) {
            return false; //  不是最新的
        }
        $update_time = self::getRedisLatestTime();
        $update_time_int = null;
        if($update_time) {
            $update_time_int = strpos($update_time, '-') ? strtotime($update_time) : $update_time;
        }
        if($update_time_int == $update_time_cache) {
            return true; //  是最新的
        }
        return false;//  不是最新的
    }

    /**
     * @return mixed
     */
    public static function getRedisLatestTime()
    {
        $class_name = get_class();
        $obj = new $class_name();
        $redis_latest_time_field = $obj->redis_latest_time_field;
        $update_time = $obj->order($redis_latest_time_field, 'desc')->value($redis_latest_time_field);
        if(!$update_time) {
            if($obj->getAutoWriteTimestamp() == 'int') {
                $obj->update([$redis_latest_time_field => time()]);
            } else {
                $obj->update([$redis_latest_time_field => date('Y-m-d H:i:s')]);
            }
        }
        $update_time = $obj->order($redis_latest_time_field, 'desc')->value($redis_latest_time_field);
        return $update_time;
    }

    /**  读取到redis数据 后，对数据的处理
     * @param array $item
     * @return array
     */
    public static function getRedisDataCheck(array $item): array
    {
        return $item;
    }

    /**
     * 设置数据到redis前，对数据的处理
     *
     * @param array $item
     * @return array
     */
    public static function setRedisDataCheck(array $item): array
    {
        return $item;
    }
}
